/**
	This file is part of Fighting Field.

	Fighting Field is free software: you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation, either version 3 of the License, or
	(at your option) any later version.

	Fighting Field is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with Fighting Field.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "Slider.h"

#include "OgreBorderPanelOverlayElement.h"
#include "OgrePanelOverlayElement.h"
#include "OgreTextAreaOverlayElement.h"

#include "GuiListener.h"

namespace FF
{
	Slider::Slider(const Ogre::String& name, const Style& style, const Ogre::DisplayString& caption, Ogre::Real width, Ogre::Real trackWidth,
		Ogre::Real valueBoxWidth, Ogre::Real minValue, Ogre::Real maxValue, unsigned int snaps)
		: mStyle(style)
		, mDragOffset(0.0f)
		, mValue(0.0f)
		, mMinValue(0.0f)
		, mMaxValue(0.0f)
		, mInterval(0.0f)
	{
		mDragging = false;
		mFitToContents = false;
		mElement = Ogre::OverlayManager::getSingleton().createOverlayElementFromTemplate
			(mStyle.templateName, "BorderPanel", name);
		mElement->setWidth(width);
		Ogre::OverlayContainer* c = (Ogre::OverlayContainer*)mElement;
		mTextArea = (Ogre::TextAreaOverlayElement*)c->getChild(GetName() + "/SliderCaption");
		Ogre::OverlayContainer* valueBox = (Ogre::OverlayContainer*)c->getChild(GetName() + "/SliderValueBox");
		valueBox->setWidth(valueBoxWidth);
		valueBox->setLeft(-(valueBoxWidth + 5));
		mValueTextArea = (Ogre::TextAreaOverlayElement*)valueBox->getChild(valueBox->getName() + "/SliderValueText");
		mTrack = (Ogre::BorderPanelOverlayElement*)c->getChild(GetName() + "/SliderTrack");
		mHandle = (Ogre::PanelOverlayElement*)mTrack->getChild(mTrack->getName() + "/SliderHandle");
		mTextArea->setCharHeight(mTextArea->getCharHeight() - 3);
		mValueTextArea->setCharHeight(mValueTextArea->getCharHeight() - 3);

		if (trackWidth <= 0)  // tall style
		{
			mTrack->setWidth(width - 16);
		}
		else  // long style
		{
			if (width <= 0) mFitToContents = true;
			mElement->setHeight(34);
			mTextArea->setTop(10);
			valueBox->setTop(2);
			mTrack->setTop(-23);
			mTrack->setWidth(trackWidth);
			mTrack->setHorizontalAlignment(Ogre::GHA_RIGHT);
			mTrack->setLeft(-(trackWidth + valueBoxWidth + 5));
		}

		SetCaption(caption);
		SetRange(minValue, maxValue, snaps, false);
	}

	void Slider::SetRange(Ogre::Real minValue, Ogre::Real maxValue, unsigned int snaps, bool notifyListener)
	{
		mMinValue = minValue;
		mMaxValue = maxValue;

		if (snaps <= 1 || mMinValue >= mMaxValue)
		{
			mInterval = 0;
			mHandle->hide();
			mValue = minValue;
			if (snaps == 1) mValueTextArea->setCaption(Ogre::StringConverter::toString(mMinValue));
			else mValueTextArea->setCaption("");
		}
		else
		{
			mHandle->show();
			mInterval = (maxValue - minValue) / (snaps - 1);
			SetValue(minValue, notifyListener);
		}
	}

	const Ogre::DisplayString& Slider::GetValueCaption()
	{
		return mValueTextArea->getCaption();
	}

	void Slider::SetValueCaption(const Ogre::DisplayString& caption)
	{
		mValueTextArea->setCaption(caption);
	}

	void Slider::SetValue(Ogre::Real value, bool notifyListener)
	{
		if (mInterval == 0) return;

		mValue = Ogre::Math::Clamp<Ogre::Real>(value, mMinValue, mMaxValue);

		SetValueCaption(Ogre::StringConverter::toString(mValue));

		if (mListener && notifyListener) mListener->OnSliderMoved(this);

		if (!mDragging) mHandle->setLeft((Ogre::Real)(int)((mValue - mMinValue) / (mMaxValue - mMinValue) *
			(mTrack->getWidth() - mHandle->getWidth())));
	}

	Ogre::Real Slider::GetValue()
	{
		return mValue;
	}

	const Ogre::DisplayString& Slider::GetCaption()
	{
		return mTextArea->getCaption();
	}

	void Slider::SetCaption(const Ogre::DisplayString& caption)
	{
		mTextArea->setCaption(caption);

		if (mFitToContents) mElement->setWidth(GetCaptionWidth(caption, mTextArea) +
			mValueTextArea->getParent()->getWidth() + mTrack->getWidth() + 26);
	}

	void Slider::_OnCursorPressed(const Ogre::Vector2& cursorPos)
	{
		if (!mHandle->isVisible()) return;

		Ogre::Vector2 co = Widget::CursorOffset(mHandle, cursorPos);

		if (co.squaredLength() <= 81)
		{
			mDragging = true;
			mDragOffset = co.x;
		}
		else if (Widget::IsCursorOver(mTrack, cursorPos))
		{
			Ogre::Real newLeft = mHandle->getLeft() + co.x;
			Ogre::Real rightBoundary = mTrack->getWidth() - mHandle->getWidth();

			mHandle->setLeft((Ogre::Real)Ogre::Math::Clamp<int>((int)newLeft, 0, (int)rightBoundary));
			SetValue(GetSnappedValue(newLeft / rightBoundary));
		}
	}

	void Slider::_OnCursorReleased(const Ogre::Vector2& cursorPos)
	{
		if (mDragging)
		{
			mDragging = false;
			mHandle->setLeft((Ogre::Real)(int)((mValue - mMinValue) / (mMaxValue - mMinValue) *
				(mTrack->getWidth() - mHandle->getWidth())));
		}
	}

	void Slider::_OnCursorMoved(const Ogre::Vector2& cursorPos)
	{
		if (mDragging)
		{
			Ogre::Vector2 co = Widget::CursorOffset(mHandle, cursorPos);
			Ogre::Real newLeft = mHandle->getLeft() + co.x - mDragOffset;
			Ogre::Real rightBoundary = mTrack->getWidth() - mHandle->getWidth();

			mHandle->setLeft((Ogre::Real)Ogre::Math::Clamp<int>((int)newLeft, 0, (int)rightBoundary));
			SetValue(GetSnappedValue(newLeft / rightBoundary));
		}
	}

	void Slider::_OnFocusLost()
	{
		mDragging = false;
	}

	Ogre::Real Slider::GetSnappedValue(Ogre::Real percentage)
	{
		percentage = Ogre::Math::Clamp<Ogre::Real>(percentage, 0, 1);
		unsigned int whichMarker = (unsigned int) (percentage * (mMaxValue - mMinValue) / mInterval + 0.5);
		return whichMarker * mInterval + mMinValue;
	}
}
